﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Core
{
    public class Board
    {
        public int Width { get; private set; }
        public Dictionary<int, List<int>> MovesFrom { get; private set; }
        public List<Tile> AllTiles { get; set; }
        private readonly Dictionary<List<int>, string> _wordCache = new Dictionary<List<int>, string>();

        public Board(int width)
        {
            Width = width;
            MovesFrom = new Dictionary<int, List<int>>();
        }

        public Board(int width, params Tile[] tiles)
            : this(width)
        {
            AllTiles = new List<Tile>(tiles);
            UpdateMovesFrom();
        }

        public Board(int width, int tileCount)
            : this(width)
        {
            AllTiles = new List<Tile>(tileCount);
            for (int i = 0; i < tileCount; i++)
                AllTiles.Add(Tiles.GetRandomTile());
            UpdateMovesFrom();
        }

        public void UpdateMovesFrom()
        {
            for (int i = 0; i < AllTiles.Count; i++)
                MovesFrom.Add(i, PossibleMovesFrom(i));
        }

        public string MovesToWord(List<int> moves)
        {
            if (_wordCache.ContainsKey(moves)) return _wordCache[moves];
            string temp = "";
            foreach (int i in moves)
            {
                temp += AllTiles[i].Character;
            }
            _wordCache.Add(moves, temp);
            return temp;
        }

        public List<int> LeftoverMoves(int index, List<int> moveHistory)
        {
            return MovesFrom[index].Except(moveHistory).ToList();
        }

        private List<int> PossibleMovesFrom(int index)
        {
            var lastIndex = AllTiles.Count - 1;
            var modulus = (2 * Width) - 1;
            var moves = new List<int>();
            if (index == 0) // Top Left
            {
                moves.Add(1);
                moves.Add(Width);
            }
            else if (index > 0 && index < Width - 1) // Top
            {
                moves.Add(index + 1);
                moves.Add(index + Width);
                moves.Add(index + Width - 1);
                moves.Add(index - 1);
            }
            else if (index == Width - 1) // Top Right
            {
                moves.Add(index + Width - 1);
                moves.Add(index - 1);
            }
            else if (index == lastIndex) // Bottom Right
            {
                moves.Add(index - 1);
                moves.Add(index - Width);
            }
            else if (index < lastIndex && index > lastIndex - Width + 1) // Bottom
            {
                moves.Add(index + 1);
                moves.Add(index - 1);
                moves.Add(index - Width);
                moves.Add(index - Width + 1);
            }
            else if (index == AllTiles.Count - Width) // Bottom Left
            {
                moves.Add(index + 1);
                moves.Add(index - Width + 1);
            }
            else if ((index + 1) % modulus == 0) // Right (short column)
            {
                moves.Add(index + Width);
                moves.Add(index + Width - 1);
                moves.Add(index - 1);
                moves.Add(index - Width);
                moves.Add(index - Width + 1);
            }
            else if ((index - Width + 1) % modulus == 0) // Right (long column)
            {
                moves.Add(index + Width - 1);
                moves.Add(index - 1);
                moves.Add(index - Width);
            }
            else if ((index - Width) % modulus == 0) // Left (short column)
            {
                moves.Add(index + 1);
                moves.Add(index + Width);
                moves.Add(index + Width - 1);
                moves.Add(index - Width);
                moves.Add(index - Width + 1);
            }
            else if (index % modulus == 0) // Left (long column)
            {
                moves.Add(index + 1);
                moves.Add(index + Width);
                moves.Add(index - Width + 1);
            }
            else // Non-edge tile
            {
                moves.Add(index + 1);
                moves.Add(index + Width);
                moves.Add(index + Width - 1);
                moves.Add(index - 1);
                moves.Add(index - Width);
                moves.Add(index - Width + 1);
            }
            return moves;
        }
    }
}